//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "MatrixTool.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <zlib.h>

#include <iostream>
#include <fstream>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LENGHTINLINE 500000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
namespace LabelsSpace {
  
  bool FindLabel(TNode <String>* NodeT, void* ref) {
    return (!strcmp(NodeT->Info()->Out(), ((String*)ref)->Out()))?true:false;
  }
  
};
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
MatrixTool::MatrixTool() {
  
  this->filenameMatrix = new String();
  this->matrix         = NULL;
  this->labels         = new TListE <String> ();
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
MatrixTool::MatrixTool(String* filenameMatrix) {
  
  this->filenameMatrix = new String(filenameMatrix);
  this->matrix         = NULL;
  this->labels         = new TListE <String> ();
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
MatrixTool::MatrixTool(char* filenameMatrix) {
  
  this->filenameMatrix = new String(filenameMatrix);
  this->matrix         = NULL;
  this->labels         = new TListE <String> ();
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
MatrixTool::MatrixTool(const char* filenameMatrix) {
  
  this->filenameMatrix = new String(filenameMatrix);
  this->matrix         = NULL;
  this->labels         = new TListE <String> ();
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
MatrixTool::MatrixTool(const MatrixTool* matrixTool) {
  
  this->filenameMatrix = new String(matrixTool->filenameMatrix);
  this->labels         = new TListE <String> (matrixTool->labels);
  
  if (matrixTool->matrix) {
    this->matrix = new double*[this->labels->Length()];
    for (long i = 0; i < this->labels->Length(); i++) {
      this->matrix[i] = new double[this->labels->Length()];
      if (matrixTool->matrix[i])
        for (long j = 0; j < this->labels->Length(); j++) 
          this->matrix[i][j] = (matrixTool->matrix[i][j])?matrixTool->matrix[i][j]:0;
    }
  }
  else this->matrix = NULL;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
MatrixTool::~MatrixTool() {

  if (this->filenameMatrix) delete this->filenameMatrix;

  if (this->matrix) {
    for (long i = 0; i < this->labels->Length(); i++)
      if (this->matrix[i]) delete[] this->matrix[i];
    delete[] this->matrix;
  }

  if (this->labels) delete this->labels;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void MatrixTool::FilenameMatrix(String* filenameMatrix) {
  
  this->filenameMatrix->In(filenameMatrix);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void MatrixTool::Matrix(double** matrix, long size) {
  
  if (!matrix || size <= 0) return;
  
  if (this->matrix) {
    for (long i = 0; i < this->labels->Length(); i++)
      if (this->matrix[i]) delete[] this->matrix[i];
    delete[] this->matrix;
  }
  
  this->matrix = new double*[size];
  for (long i = 0; i < size; i++) {
    this->matrix[i] = new double[size];
    if (matrix[i])
      for (long j = 0; j < size; j++) 
        this->matrix[i][j] = (matrix[i][j])?matrix[i][j]:0;
  }
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void MatrixTool::Labels(TListE <String>* labels) {
  
  *(this->labels) = *(labels);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* MatrixTool::FilenameMatrix(void) {
  
  return this->filenameMatrix;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double** MatrixTool::Matrix(void) {
  
  return this->matrix;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE <String>* MatrixTool::Labels(void) {
  
  return this->labels;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool MatrixTool::ReadFile(void) {
  
  if (!this->filenameMatrix || !this->filenameMatrix->Length()) 
    return false;
  
  ifstream         matrixFile;
  gzFile           matrixFileGZ;
  
  String           lineInput;
  long             counter = 0;
  
  TListE <String>* listValues = new TListE <String> ();
  
  bool             fileIsGZ = (this->filenameMatrix->Contain("gz"))?true:false;

  if (!fileIsGZ) matrixFile.open(this->filenameMatrix->Out(), ios::in);
  else           matrixFileGZ = gzopen(this->filenameMatrix->Out(), "rb9");

  while (!((!fileIsGZ)?matrixFile.eof():gzeof(matrixFileGZ))) {

    char buffer[LENGHTINLINE + 1];

    if (!fileIsGZ) matrixFile.getline(buffer, LENGHTINLINE);
    else           gzgets(matrixFileGZ, buffer, LENGHTINLINE);
    
    cout << "Read line: " << counter << "\r";
    
    lineInput.In(buffer); lineInput.Trim();

    if (lineInput.Length()) {

      if (!counter) { 
        
        if (this->matrix) {
          for (long i = 0; i < this->labels->Length(); i++)
            if (this->matrix[i]) delete[] this->matrix[i];
          delete[] this->matrix;
        }
        
        this->labels->Clear();
        
        this->SplitColumn(lineInput.Out(), this->labels, " \t\n");
        this->matrix = new double*[this->labels->Length()];
        for (long i = 0; i < this->labels->Length(); i++) 
          this->matrix[i] = new double[this->labels->Length()];
        
      }
      else {
        
        listValues->Clear();
        
        this->SplitColumn(lineInput.Out(), listValues, " \t\n");
        for (long i = 0; i < this->labels->Length(); i++)
          this->matrix[counter - 1][i] = (i < listValues->Length())?atof(listValues->Get(i)->Out()):0;

      }
      
      counter++;

    }

  }
  
  if (!fileIsGZ) matrixFile.close();
  else           gzclose(matrixFileGZ);
  
  if (listValues) delete listValues;
  
  return true;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool MatrixTool::ReadFromString(TListE <String>* labelsMatrix, String* textMatrix) {
  
  if (!labelsMatrix || !labelsMatrix->Length() || !textMatrix || !textMatrix->Length()) 
    return false;
  
  long             counter    = 0;
  
  TListE <String>* lines      = new TListE <String> ();
  TListE <String>* listValues = new TListE <String> ();
  
  if (this->matrix) {
    for (long i = 0; i < this->labels->Length(); i++)
      if (this->matrix[i]) delete[] this->matrix[i];
    delete[] this->matrix;
  }

  this->labels->Clear();
  
  *(this->labels) = *labelsMatrix;

  this->matrix = new double*[this->labels->Length()];
  for (long i = 0; i < this->labels->Length(); i++)
    this->matrix[i] = new double[this->labels->Length()];
  
  this->SplitColumn(textMatrix->Out(), lines, "\n");

  lines->SetInitial();
  while (lines->SetNext()) {

    lines->GetCurrent()->Trim();
    if (lines->GetCurrent()->Length()) {
      
      listValues->Clear();

      this->SplitColumn(lines->GetCurrent()->Out(), listValues, " \t\n");
      for (long i = 0; i < this->labels->Length(); i++)
        this->matrix[counter][i] = (i < listValues->Length())?atof(listValues->Get(i)->Out()):0;
      
      counter++;

    }

  }
  
  if (lines)      delete lines;
  if (listValues) delete listValues;
  
  return true;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool MatrixTool::ReadFromString(TListE <String>* labelsMatrix, char* textMatrix) {
  
  if (!labelsMatrix || !labelsMatrix->Length() || !textMatrix || !strlen(textMatrix)) 
    return false;
  
  String* stringMatrix = new String (textMatrix);
  bool    result       = this->ReadFromString(labelsMatrix, stringMatrix);
  
  if (stringMatrix) delete stringMatrix;
  
  return result;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool MatrixTool::GetSubMatrix(long posIni, long posEnd, double** matrixOut) {
  
  if (!this->matrix || !this->labels->Length() || posIni > posEnd) 
    return false;
  
  posIni = (posIni < 0)?0:posIni;
  posEnd = (posEnd >= this->labels->Length())?(this->labels->Length() - 1):posEnd;
  
  long sizeOut = posEnd - posIni + 1;
  
  matrixOut = new double*[sizeOut];
  for (long i = posIni, j = 0; i <= posEnd; i++, j++) {
    matrixOut[j] = new double[sizeOut];
    for (long k = posIni, z = 0; k <= posEnd; k++, z++)
      matrixOut[j][z] = (this->matrix[i] && this->matrix[i][k])?this->matrix[i][k]:0;
  }
  
  return true;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
char* MatrixTool::GetSubMatrix(long posIni, long posEnd) {
  
  if (!this->matrix || !this->labels->Length() || posIni > posEnd) 
    return NULL;
  
  posIni = (posIni < 0)?0:posIni;
  posEnd = (posEnd >= this->labels->Length())?(this->labels->Length() - 1):posEnd;
  
  char*  text    = NULL;
  FILE*  tmpFile = tmpfile();
  long   size;
  
  for (long i = posIni; i <= posEnd; i++) {
    for (long j = posIni; j <= posEnd; j++) 
      fprintf(tmpFile, "%.6f \0", (this->matrix[i] && this->matrix[i][j])?this->matrix[i][j]:0); 
    fprintf(tmpFile, "\n");
  }
  
  fseek(tmpFile, 0, SEEK_END);
  size = ftell(tmpFile);
  rewind(tmpFile);
  
  text = new char[size + 1];
  fread(text, 1, size, tmpFile);
  
  fclose(tmpFile);
  
  return text;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool MatrixTool::GetSubMatrix(TListE <String>* searhList, double** matrixOut) {
  
  if (!this->matrix || !this->labels->Length() || !searhList || !searhList->Length()) 
    return false;
  
  TListE <long>* listPositions = new TListE <long> ();
  long           position;
  
  searhList->SetInitial();
  while (searhList->SetNext()) 
    if ((position = this->labels->FirstOcurrencePosition(LabelsSpace::FindLabel, searhList->GetCurrent())) > 0)
      listPositions->Add(new long(position));
    else { searhList->DeleteCurrent(); searhList->SetPrevious(); }
 
  matrixOut = new double*[listPositions->Length()];
  
  for (long i = 0; i < listPositions->Length(); i++) {
    
    matrixOut[i] = new double[listPositions->Length()];
    
    for (long j = 0; j < listPositions->Length(); j++)
      matrixOut[i][j] = (this->matrix[*(listPositions->Get(i))] && 
                         this->matrix[*(listPositions->Get(i))][*(listPositions->Get(j))])
                        ?this->matrix[*(listPositions->Get(i))][*(listPositions->Get(j))]
                        :0;
  }
  
  
  if (listPositions) delete listPositions;
  
  return true;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
char* MatrixTool::GetSubMatrix(TListE <String>* searhList) {
  
  String textOut;
  
  if (!this->matrix || !this->labels->Length() || !searhList || !searhList->Length()) 
    return NULL;
  
  TListE <long>* listPositions = new TListE <long> ();
  long           position;
  
  searhList->SetInitial();
  while (searhList->SetNext()) 
    if ((position = this->labels->FirstOcurrencePosition(LabelsSpace::FindLabel, searhList->GetCurrent())) > 0)
      listPositions->Add(new long(position));
    else { searhList->DeleteCurrent(); searhList->SetPrevious(); }
 
  char*  text    = NULL;
  FILE*  tmpFile = tmpfile();
  long   size;
  
  
  for (long i = 0; i < listPositions->Length(); i++) {
    for (long j = 0; j < listPositions->Length(); j++) 
      fprintf(tmpFile, "%.6f \0", (this->matrix[*(listPositions->Get(i))] && 
                                   this->matrix[*(listPositions->Get(i))][*(listPositions->Get(j))])
                                  ?this->matrix[*(listPositions->Get(i))][*(listPositions->Get(j))]
                                  :0);
    fprintf(tmpFile, "\n");
  }
  
  
  fseek(tmpFile, 0, SEEK_END);
  size = ftell(tmpFile);
  rewind(tmpFile);
  
  text = new char[size + 1];
  fread(text, 1, size, tmpFile);
  
  fclose(tmpFile);

  if (listPositions) delete listPositions;
  
  return text;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool MatrixTool::ValuePosition(String* label1, String* label2, double &value) {
  
  long position1 = this->labels->FirstOcurrencePosition(LabelsSpace::FindLabel, label1);
  long position2 = this->labels->FirstOcurrencePosition(LabelsSpace::FindLabel, label2);
  
  if (position1 == -1 || position2 == -1) return false;
  
  value = (this->matrix && this->matrix[position1] && this->matrix[position1][position2])?this->matrix[position1][position2]:0;
  
  return true;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool MatrixTool::SplitColumn(char* buffer, TListE <String>* columns, char* characters) {

  if (!buffer || !columns || !characters) return false;

  char *part = strtok(buffer, characters);

  while (part) {
    String* cell = new String(part);
    cell->Trim();
    columns->Add(cell);
    part = strtok(NULL, characters);
  }

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool MatrixTool::SplitColumn(char* buffer, TListE <String>* columns, const char* characters) {

  if (!buffer || !columns || !characters) return false;

  char *part = strtok(buffer, characters);

  while (part) {
    String* cell = new String(part);
    cell->Trim();
    columns->Add(cell);
    part = strtok(NULL, characters);
  }

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
